package com.cellngine.server;

import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;

import com.cellngine.Constants;
import com.cellngine.JO;
import com.cellngine.cellngine;

/**
 * cellngine Server Port
 * 
 * Allows clients to connect to this server on a specified port.
 * Optionally, the server can be password-protected to grant access only to clients providing the
 * correct password.
 * 
 * @author Felix Kirchmann <qwer@cellngine.com>
 */
public class ServerPort extends Thread
{
	private final ServerManager	master;
	private ServerSocket		sock;
	private final int			port;
	private boolean				active	= false;
	
	/**
	 * Creates a new server and defines the port number, but doesn't open the listening socket.
	 * 
	 * @param port
	 *            The port number the server shall listen on. Must be larger than 0 but not larger
	 *            than 65535. If the port number is not within the specified range, the server will
	 *            instead listen on ServerPort.defaultPort;
	 */
	public ServerPort(final ServerManager master, final int port)
	{
		this.master = master;
		if (!JO.isPortValid(port))
		{
			cellngine.log("Invalid port number " + port + " specified, setting port to default ("
					+ Constants.default_port + ")", Constants.log_type.info);
			this.port = Constants.default_port;
		}
		else
		{
			this.port = port;
		}
	}
	
	/**
	 * Makes the server accept connections on the previously defined port.
	 */
	@Override
	public void run()
	{
		active = true;
		
		try
		{
			sock = new ServerSocket(port);
			cellngine.log("Server listening on port " + port, Constants.log_type.message);
			Socket socket;
			while (active)
			{
				socket = sock.accept();
				ServerClientSocket sSocket;
				if (active)
				{
					sSocket = new ServerClientSocket(master, socket);
					master.addConnection(sSocket);
					new Thread(sSocket).start();
				}
			}
		}
		catch (final BindException e)
		{
			cellngine.log("Unable to listen on port " + port
					+ ", port is already bound by another program.", Constants.log_type.error);
			active = false;
		}
		catch (final Exception e)
		{
			active = false;
			cellngine.log(e, Constants.log_type.error);
		}
		active = false;
	}
	
	/**
	 * @return Returns whether this ServerPort is accepting connections on the specified
	 *         <code>port</code>.
	 */
	public boolean isActive()
	{
		if (sock == null)
		{
			return false;
		}
		return active && sock.isBound();
	}
	
	/**
	 * Causes this ServerPort to close all active connections and stop waiting for connections.
	 */
	public void stopListening()
	{
		active = false;
	}
}